{ input output Factory }
unit hadeobjectfactory;

{$I ..\hddefines.inc}
{$mode objfpc}{$H+}

interface

uses
  Classes,
  SysUtils,
  hadebasevisitor,
  hadebaseobject,
  hadedataset,
  hademapping;

type

  { IHadeObjectFactory }

  IHadeObjectFactory = interface
    ['{3A10C05E-AE01-4BD1-B5C2-FFEBAC17270A}']
    procedure ToObject(const AObject: THadeVisited; const AttrName: string);
    procedure ToRow(const AObject: THadeVisited; const AttrName: string);
  end;

  { THadeIOPropAbs }

  THadeIOPropAbs = class(THadeBaseObject, IHadeObjectFactory)
  protected
    FDataSet: THadeDataSet;
    FMappingManager:THadeMappingManager;
    procedure setOIDFK(const AObject: THadeVisited; const AttrName: string);
    function ColNameByAttr(const AObject: THadeVisited; const AttrName: string): string;
  public
    constructor Create(const AMappingManager:THadeMappingManager;
      const ADataSet: THadeDataSet);
    destructor Destroy; override;

    procedure ToObject(const AObject: THadeVisited; const AttrName: string);
      virtual; abstract;
    procedure ToRow(const AObject: THadeVisited; const AttrName: string);
      virtual; abstract;
  end;

  { THadeIOPropString }

  THadeIOPropString = class(THadeIOPropAbs)
    procedure ToObject(const AObject: THadeVisited; const AttrName: string); override;
    procedure ToRow(const AObject: THadeVisited; const AttrName: string); override;
  end;

  { THadeIOPropInteger }

  THadeIOPropInteger = class(THadeIOPropAbs)
    procedure ToObject(const AObject: THadeVisited; const AttrName: string); override;
    procedure ToRow(const AObject: THadeVisited; const AttrName: string); override;
  end;

  { THadeIOPropDouble }

  THadeIOPropDouble = class(THadeIOPropAbs)
    procedure ToObject(const AObject: THadeVisited; const AttrName: string); override;
    procedure ToRow(const AObject: THadeVisited; const AttrName: string); override;
  end;

  { THadeIOPropDateTime }

  THadeIOPropDateTime = class(THadeIOPropAbs)
    procedure ToObject(const AObject: THadeVisited; const AttrName: string); override;
    procedure ToRow(const AObject: THadeVisited; const AttrName: string); override;
  end;

  { THadeIOPropClass }

  THadeIOPropClass = class(THadeIOPropAbs)
  public
    procedure ToObject(const AObject: THadeVisited; const AttrName: string); override;
    procedure ToRow(const AObject: THadeVisited; const AttrName: string); override;
  end;

  { THadeIOPropEnum }

  THadeIOPropEnum = class(THadeIOPropAbs)
    procedure ToObject(const AObject: THadeVisited; const AttrName: string); override;
    procedure ToRow(const AObject: THadeVisited; const AttrName: string); override;
  end;

  { THadeIOPropVar }

  THadeIOPropVar = class(THadeIOPropAbs)
    procedure ToObject(const AObject: THadeVisited; const AttrName: string); override;
    procedure ToRow(const AObject: THadeVisited; const AttrName: string); override;
  end;

  { THadeIOPropInterface }

  THadeIOPropInterface = class(THadeIOPropAbs)
    procedure ToObject(const AObject: THadeVisited; const AttrName: string); override;
    procedure ToRow(const AObject: THadeVisited; const AttrName: string); override;
  end;

  { THadeIOManager }

  THadeIOManager = class
  private
    IFactory: IHadeObjectFactory;
    FMappingManager:THadeMappingManager;
  protected
    procedure CreateFactory(const AObject: THadeVisited; const AttrName: string;
      const AdataSet: THadeDataSet);
  public
    procedure ToObject(const AObject: THadeVisited; const AttrName: string;
      const AdataSet: THadeDataSet);
    procedure ToRow(const AObject: THadeVisited; const AttrName: string;
      const AdataSet: THadeDataSet);

    procedure RowToObject(const AObject: THadeVisited; const AdataSet: THadeDataSet);
    procedure RowToObjectList(const AObject: THadeVisited; const AdataSet: THadeDataSet);
    procedure ObjectToRow(const AObject: THadeVisited; const AdataSet: THadeDataSet);

    constructor create(const AMappingManager:THadeMappingManager);
  end;

implementation

uses
  hadertti,
  typinfo,
  hadeoid,
  hadeopfmanager,
  hadeobject;

{ THadeIOPropInterface }

procedure THadeIOPropInterface.ToObject(const AObject: THadeVisited;
  const AttrName: string);
var
  Value: THdOID;
begin
  Value := GHadeOID.getOID;
  if GHadeOPFManager.OIDStrategy = oidAUTOInc then
    Value.IntegerOID := FDataSet.IntegerByName[ColNameByAttr(AObject, AttrName)]
  else
    Value.StringOID := FDataSet.StringByName[ColNameByAttr(AObject, AttrName)];
  hadertti.sethdInterfaceProp(AObject, AttrName, Value);
end;

procedure THadeIOPropInterface.ToRow(const AObject: THadeVisited;
  const AttrName: string);
var
  colName: string;
  obj: THdOID;
begin
  colName := ColNameByAttr(AObject, AttrName);
  obj := THdOID(hadertti.getHdInterfaceProp(AObject, AttrName));

  if obj.isNUll then
  begin
    FDataSet.ValueByName[colName] := NULL;
    exit;
  end;
  case GHadeOPFManager.OIDStrategy of
    oidAutoInc: FDataSet.IntegerByName[colName] := obj.IntegerOID;
    oidGUID: FDataSet.StringByName[colName] := obj.StringOID;
    oidUserDefined: FDataSet.ValueByName[colName] := obj.getValue;
  end;
end;

{ THadeIOPropClass }
procedure THadeIOPropClass.ToObject(const AObject: THadeVisited; const AttrName: string);
var
  ClassMap: THadeMapClass;
  obj: THadeObject;
begin
  ClassMap := FMappingManager.ClassByName(AObject.ClassName);
  if ptkFK in ClassMap.PropByName(AttrName).Column.ColInfo then
  begin
    obj := THadeObjectClass(hadertti.getHdObjectClassProp(AObject, AttrName)).Create(AObject);
    setOIDFK(obj, ColNameByAttr(AObject, AttrName));
    hadertti.sethdObjectProp(AObject, AttrName, obj);
    if not obj.OID.isNUll then
      obj.MarkPK();
  end;
end;

procedure THadeIOPropClass.ToRow(const AObject: THadeVisited; const AttrName: string);
var
  colName: string;
  obj: THadeObject;
begin
  colName := ColNameByAttr(AObject, AttrName);
  obj := THadeObject(hadertti.getHdObjectProp(AObject, AttrName));
  if not Assigned(obj) then
  begin
    FDataSet.ValueByName[colName] := NULL;
  end
  else
  begin
    if obj.OID.isNUll then
    begin
      FDataSet.ValueByName[colName] := NULL;
      exit;
    end;
    case GHadeOPFManager.OIDStrategy of
      oidAutoInc: FDataSet.IntegerByName[colName] := obj.OID.IntegerOID;
      oidGUID: FDataSet.StringByName[colName] := obj.OID.StringOID;
      oidUserDefined: FDataSet.ValueByName[colName] := obj.OID.getValue;
    end;
  end;
end;

{ THadeIOPropVar }

procedure THadeIOPropVar.ToObject(const AObject: THadeVisited; const AttrName: string);
var
  Value: variant;
begin
  Value := FDataSet.valueByName[ColNameByAttr(AObject, AttrName)];
  hadertti.setVarProp(AObject, AttrName, Value);
end;

procedure THadeIOPropVar.ToRow(const AObject: THadeVisited; const AttrName: string);
var
  colName: string;
begin
  colName := ColNameByAttr(AObject, AttrName);
  FDataSet.valueByName[colName] := hadertti.getVarProp(AObject, AttrName);
end;

{ THadeIOManager }

procedure THadeIOManager.CreateFactory(const AObject: THadeVisited;
  const AttrName: string; const AdataSet: THadeDataSet);
begin
  if hadertti.IsStringProp(AObject, AttrName) then
  begin
    IFActory := THadeIOPropString.Create(FMappingManager,ADataSet);
  end
  else
  begin
    case typinfo.PropType(AObject, AttrName) of
      tkinteger: IFActory := THadeIOPropInteger.Create(FMappingManager,ADataSet);
      tkint64: IFActory := THadeIOPropInteger.Create(FMappingManager,ADataSet);
      tkEnumeration: IFActory := THadeIOPropEnum.Create(FMappingManager,ADataSet);
      tkFloat: IFActory := THadeIOPropDouble.Create(FMappingManager,ADataSet);
      tkClass: IFActory := THadeIOPropClass.Create(FMappingManager,ADataSet);
      tkInterface: IFactory := THadeIOPropInterface.Create(FMappingManager,ADataSet);
      else
        IFactory := THadeIOPropVar.Create(FMappingManager,ADataSet);
    end;
  end;
end;

procedure THadeIOManager.ToObject(const AObject: THadeVisited;
  const AttrName: string; const AdataSet: THadeDataSet);
begin
  self.CreateFactory(AObject, AttrName, ADataSet);
  IFactory.ToObject(AObject, AttrName);
end;

procedure THadeIOManager.ToRow(const AObject: THadeVisited; const AttrName: string;
  const AdataSet: THadeDataSet);
begin
  self.CreateFactory(AObject, AttrName, ADataSet);
  IFactory.ToRow(AObject, AttrName);
end;

procedure THadeIOManager.RowToObject(const AObject: THadeVisited;
  const AdataSet: THadeDataSet);
var
  iloop: integer;
  listread: TStringList;
  MapClass: THadeMapClass;
begin
  MapClass := FMappingManager.ClassByName(AObject.ClassName);
  listread := MapClass.getReadableProps;
  try
    for iloop := 0 to pred(listread.Count) do
    begin
      ToObject(AObject, listread[iloop], AdataSet);
    end;
  finally
    FreeAndNil(listread);
  end;
  THadeObject(AObject).MarkClean();
end;

procedure THadeIOManager.RowToObjectList(const AObject: THadeVisited;
  const AdataSet: THadeDataSet);
var
  iloop: integer;
  objName: string;
  obj: THadeObject;
begin
  objName := FMappingManager.RegObjecByName(AObject.ClassName).ClassBOName;

  for iloop := 0 to pred(ADataset.Count) do
  begin
    obj := THadeObjectCLass(FMappingManager.ClassByName(
      objName).SelfRef).Create(AObject);
    RowToObject(obj, ADataset);
    ADataSet.Next;
    THadeObjectList(AObject).add(obj);
  end;
end;

procedure THadeIOManager.ObjectToRow(const AObject: THadeVisited;
  const AdataSet: THadeDataSet);
var
  iloop: integer;
  listread: TStringList;
  MapClass: THadeMapClass;
begin
  MapClass := FMappingManager.ClassByName(AObject.ClassName);
  listread := MapClass.getWriteableProps;
  try
    for iloop := 0 to pred(listread.Count) do
    begin
      ToROW(AObject, listread[iloop], AdataSet);
    end;
  finally
    FreeAndNil(listread);
  end;
end;

constructor THadeIOManager.create(const AMappingManager: THadeMappingManager);
begin
  FMappingManager:=AMappingManager;
end;

{ THadeIOPropEnum }

procedure THadeIOPropEnum.ToObject(const AObject: THadeVisited; const AttrName: string);
var
  Value: integer;
begin
  Value := FDataSet.IntegerByName[ColNameByAttr(AObject, AttrName)];
  hadertti.setOrdinalProp(AObject, AttrName, Value);
end;

procedure THadeIOPropEnum.ToRow(const AObject: THadeVisited; const AttrName: string);
var
  colName: string;
begin
  colName := self.ColNameByAttr(AObject, AttrName);
  FDataSet.IntegerByName[colName] := hadertti.getOrdinalProp(AObject, AttrName);
end;

{ THadeIOPropDouble }

procedure THadeIOPropDouble.ToObject(const AObject: THadeVisited;
  const AttrName: string);
var
  Value: double;
begin
  Value := FDataSet.DoubleByName[ColNameByAttr(AObject, AttrName)];
  hadertti.setFloatProp(AObject, AttrName, Value);
end;

procedure THadeIOPropDouble.ToRow(const AObject: THadeVisited; const AttrName: string);
var
  colName: string;
begin
  colName := self.ColNameByAttr(AObject, AttrName);
  FDataSet.DoubleByName[colName] := hadertti.getFloatProp(AObject, AttrName);
end;

{ THadeIOPropInteger }

procedure THadeIOPropInteger.ToObject(const AObject: THadeVisited;
  const AttrName: string);
var
  Value: integer;
begin
  Value := FDataSet.IntegerByName[ColNameByAttr(AObject, AttrName)];
  hadertti.setIntegerProp(AObject, AttrName, Value);
end;

procedure THadeIOPropInteger.ToRow(const AObject: THadeVisited; const AttrName: string);
var
  colName: string;
begin
  colName := self.ColNameByAttr(AObject, AttrName);
  FDataSet.IntegerByName[colName] := hadertti.getIntegerProp(AObject, AttrName);
end;

{ THadeIOPropDateTime }

procedure THadeIOPropDateTime.ToObject(const AObject: THadeVisited;
  const AttrName: string);
var
  Value: double;
begin
  Value := FDataSet.DateTimeByName[ColNameByAttr(AObject, AttrName)];
  hadertti.setFloatProp(AObject, AttrName, Value);
end;

procedure THadeIOPropDateTime.ToRow(const AObject: THadeVisited; const AttrName: string);
var
  colName: string;
begin
  colName := self.ColNameByAttr(AObject, AttrName);
  FDataSet.DateTimeByName[colName] := hadertti.getFloatProp(AObject, AttrName);
end;

{ THadeIOPropString }

procedure THadeIOPropString.ToObject(const AObject: THadeVisited;
  const AttrName: string);
var
  Value: string;
begin
  Value := FDataSet.StringByName[ColNameByAttr(AObject, AttrName)];
  hadertti.setStringProp(AObject, AttrName, Value);
end;

procedure THadeIOPropString.ToRow(const AObject: THadeVisited; const AttrName: string);
var
  colName: string;
begin
  colName := self.ColNameByAttr(AObject, AttrName);
  FDataSet.StringByName[colName] := hadertti.getStringProp(AObject, AttrName);
end;

{ THadeIOPropAbs }

procedure THadeIOPropAbs.setOIDFK(const AObject: THadeVisited; const AttrName: string);
var
  Value: THdOID;
  pk: string;
begin
  pk := FMappingManager.ClassByName(AObject.ClassName).ColNameByProp('OID');
  Value := GHadeOID.getOID;
  if GHadeOPFManager.OIDStrategy = oidAUTOInc then
    Value.IntegerOID := FDataSet.IntegerByName[AttrName]
  else if GHadeOPFManager.OIDStrategy = oidGUID then
    Value.StringOID := FDataSet.StringByName[AttrName]
  else
    Value.setOIDValue(FDataSet.StringByName[AttrName]);
  hadertti.sethdInterfaceProp(AObject, pk, Value);
end;

function THadeIOPropAbs.ColNameByAttr(const AObject: THadeVisited;
  const AttrName: string): string;
begin
  Result := FMappingManager.ClassByName(
    Aobject.ClassName).ColNameByProp(AttrName);
end;

constructor THadeIOPropAbs.Create(const AMappingManager: THadeMappingManager;
  const ADataSet: THadeDataSet);
begin
  FDataSet := ADataSet;
  FMappingManager:=AMappingManager;
end;

destructor THadeIOPropAbs.Destroy;
begin
  //FreeAndNil(FDataSet);
  inherited Destroy;
end;

end.

